package biz.datalk.industrialland.common.log.util;

import biz.datalk.commons.utils.ip.IPUtil;
import biz.datalk.commons.utils.json.JsonUtil;
import biz.datalk.industrialland.common.advice.AppSecurityInfo;
import biz.datalk.industrialland.common.advice.cfg.DatalkCryptoCfgs;
import biz.datalk.industrialland.common.component.AppEncryptSecurityKeyComponent;
import biz.datalk.industrialland.common.def.CommonConstant;
import biz.datalk.industrialland.common.encrypt.AESTool;
import biz.datalk.industrialland.common.encrypt.RSATool;
import biz.datalk.industrialland.common.encrypt.sm.GMUtils;
import biz.datalk.industrialland.common.log.aspect.ReqBodyCryptoDTO;
import biz.datalk.industrialland.common.log.def.LogTypeEnum;
import biz.datalk.industrialland.common.log.def.OptEnum;
import biz.datalk.industrialland.common.log.def.OptResultEnum;
import biz.datalk.industrialland.common.log.pojo.bo.LogBO;
import biz.datalk.industrialland.common.util.RequestHeaderUtil;
import biz.datalk.industrialland.common.util.SpringContextUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author tarofang@163.com
 * @date 2020-11-24
 */
public class SysLogUtil {
    private static final Logger log = LoggerFactory.getLogger(SysLogUtil.class);

    public static String getReqBodyParam(HttpServletRequest request) {
        String bodyParams = StringUtils.EMPTY;
        try {
            String isUpload = String.valueOf(request.getAttribute(CommonConstant.FILE_REQ_ATTR_IS_UPLOAD_API_KEY));
            String isDownload = String.valueOf(request.getAttribute(CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_KEY));
            if (StringUtils.equals(CommonConstant.FILE_REQ_ATTR_IS_UPLOAD_API_VAL, isUpload) //
                    || StringUtils.equals(CommonConstant.FILE_REQ_ATTR_IS_DOWNLOAD_API_VAL, isDownload) ) {
                return bodyParams;
            }

            String contentType = request.getContentType();
            if (StringUtils.startsWith(StringUtils.lowerCase(contentType), "multipart/")) {
                return StringUtils.EMPTY;
            }

            bodyParams = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        } catch (Exception ex) {
            log.error("{}", ex.getMessage(), ex);
        }
        return bodyParams;
    }

    public static String getReqUrlParam(HttpServletRequest request) {
        Enumeration<String> parameterNames = request.getParameterNames();
        Map<String, String> urlParams = new HashMap<>();
        String paramName;
        while (parameterNames.hasMoreElements()) {
            paramName = parameterNames.nextElement();
            urlParams.put(paramName, request.getParameter(paramName));
        }
        return urlParams.isEmpty() ? StringUtils.EMPTY : JsonUtil.getJsonFromObject(urlParams, Boolean.TRUE);
    }

    public static LogBO pkgLogBo(HttpServletRequest request, long sysUserId, String username, LogTypeEnum logTypeEnum, //
                                OptEnum optEnum, String logContent,OptResultEnum opResult, String reqResult, //
                                String reqBodyPlaintext, long costMillisecond, String logServerIp) {
        // 客户端IP
        String optIp = IPUtil.getIpAddress(request);

        Date now = new Date();
        String optTime = DateFormatUtils.format(now, "yyyy-MM-dd HH:mm:ss");

        // 获取客户端信息
        String clientType = StringUtils.trimToEmpty(request.getHeader(CommonConstant.X_CLIENT_TYPE));
        String clientVer = StringUtils.trimToEmpty(request.getHeader(CommonConstant.X_CLIENT_VER));
        String clientId = StringUtils.trimToEmpty(request.getHeader(CommonConstant.X_CLIENT_ID));

        // 获取请求/响应相关数据
        String reqUrl = request.getRequestURL().toString();
        String reqMethod = request.getMethod();
        String reqUrlParam = getReqUrlParam(request);
        String reqBodyParam = getReqBodyParam(request);

        // 判定是否是 开放平台的请求
        String appkey = "";
        String accessToken = "";
        String reqResultIv = "";
        String reqResultAesKey = "";
        String reqResultToken = "";
        String openReqResultData = "";
        // if (ModuleEnum.OPEN.equals(optEnum.getModuleType().getModule())) {
        //     accessToken = StringUtils.trimToEmpty(request.getParameter(CommonConstant.URL_ACCESS_TOKEN));
        //     appkey = LogOauth2Util.getAppkeyFromAccessToken(accessToken);
        //     appkey = StringUtils.trimToEmpty(appkey);
        //     reqResultIv = (String) request.getAttribute(CommonConstant.REQ_ATTR_CUS_SYS_LOG_REQ_RESULT_IV);
        //     reqResultAesKey = (String) request.getAttribute(CommonConstant.REQ_ATTR_CUS_SYS_LOG_REQ_RESULT_AES_KEY);
        //     reqResultToken = (String) request.getAttribute(CommonConstant.REQ_ATTR_CUS_SYS_LOG_REQ_RESULT_CALL_BACK_TOKEN);
        //     reqResultIv = StringUtils.trimToEmpty(reqResultIv);
        //     reqResultAesKey = StringUtils.trimToEmpty(reqResultAesKey);
        //     reqResultToken = StringUtils.trimToEmpty(reqResultToken);
        //     openReqResultData = (String) request.getAttribute(CommonConstant.REQ_ATTR_CUS_SYS_LOG_REQ_RESULT_DATA);
        //     openReqResultData = StringUtils.trimToEmpty(openReqResultData);
        //     // 尝试从 url 中获取 appkey
        //     if (StringUtils.isBlank(appkey)) {
        //         appkey = StringUtils.trimToEmpty(request.getParameter("appkey"));
        //     }
        // }

        // 封装入库对象
        LogBO logBO = new LogBO();
        // logBO.setId(0L);
        logBO.setLogType(logTypeEnum.getLogType());
        logBO.setOptUserId(String.valueOf(sysUserId));
        logBO.setOptUsername(StringUtils.trimToEmpty(username));
        logBO.setOptIp(optIp);
        logBO.setOptTime(optTime);
        logBO.setOptModuleCode(optEnum.getModuleType().getModule().getModuleCode());
        logBO.setOptModuleName(optEnum.getModuleType().getModule().getDescribe());
        logBO.setOptTypeCode(optEnum.getModuleType().getModuleTypeCode());
        logBO.setOptTypeName(optEnum.getModuleType().getDescribe());
        logBO.setOptCode(optEnum.getOptCode());
        logBO.setOptName(optEnum.getDescribe());
        logBO.setOptResult(opResult.getCode());
        logBO.setOptServerIp(StringUtils.trimToEmpty(logServerIp));
        logBO.setClientType(clientType);
        logBO.setClientVer(clientVer);
        logBO.setClientId(clientId);
        logBO.setLogContent(StringUtils.trimToEmpty(logContent));
        logBO.setCostMillisecond(costMillisecond);
        logBO.setCreateTime(now);
        logBO.setReqUrl(reqUrl);
        logBO.setReqMethod(reqMethod);
        logBO.setReqUrlParam(reqUrlParam);
        logBO.setReqBodyParam(reqBodyParam);
        logBO.setReqResult(reqResult);
        logBO.setReqBodyPlaintext(reqBodyPlaintext);

        logBO.setAppKey(appkey);
        logBO.setAccessToken(accessToken);

        logBO.setReqResultIv(reqResultIv);
        logBO.setReqResultAesKey(reqResultAesKey);
        logBO.setReqResultToken(reqResultToken);

        logBO.setOpenReqResultData(openReqResultData);

        return logBO;
    }

    public static String decryptReqBody(String reqBody, String serverPrivateKey) {
        serverPrivateKey = StringUtils.trimToNull(serverPrivateKey);

        if (StringUtils.isBlank(reqBody) || StringUtils.isEmpty(serverPrivateKey)) {
            return StringUtils.EMPTY;
        }

        try {
            ReqBodyCryptoDTO cryptoDTO = JsonUtil.json2JavaBean(reqBody, ReqBodyCryptoDTO.class);
            String appKey = cryptoDTO == null ? StringUtils.EMPTY : cryptoDTO.getAppkey();
            String enkey = cryptoDTO == null ? null : cryptoDTO.getEnkey();
            String endata = cryptoDTO == null ? null : cryptoDTO.getEndata();
            if (StringUtils.isAnyBlank(enkey, endata)) {
                return StringUtils.EMPTY;
            }

            // add 2022-06-29 by tarofang@163.com S
            // 判定是否使用国密加密
            try {
                String clientType = RequestHeaderUtil.getXClientTypeStr();
                int clientVer = RequestHeaderUtil.getXClientVer();
                DatalkCryptoCfgs datalkCryptoCfgs = SpringContextUtils.getBean(DatalkCryptoCfgs.class);
                boolean useGm = datalkCryptoCfgs.useGm(clientType, clientVer);
                if (useGm) {
                    AppEncryptSecurityKeyComponent appEncryptSecurityKeyComponent = SpringContextUtils.getBean(AppEncryptSecurityKeyComponent.class);
                    AppSecurityInfo byAppKey = appEncryptSecurityKeyComponent.getByAppKey(appKey);
                    String gmServerPrivateKey = datalkCryptoCfgs.getGmServer().getPrivateKey();
                    if (byAppKey != null) {
                        gmServerPrivateKey = byAppKey.getServerPrivateKey();
                        if (StringUtils.isBlank(serverPrivateKey)) {
                            log.warn("serverPrivateKey is blank. [appkey={}]", appKey);
                        }
                    }
                    String sm4Key = GMUtils.sm2Decrypt(gmServerPrivateKey, enkey);
                    // String sm4Key = GMUtils.sm2Decrypt(datalkCryptoCfgs.getGmServer().getPrivateKey(), enkey);
                    return GMUtils.sm4Decrypt(endata, sm4Key);
                }
            } catch (Exception ex) {
                if (log.isDebugEnabled()) {
                    log.debug("{}", ex.getMessage());
                }
            }
            // add 2022-06-29 by tarofang@163.com E


            String aseKey = RSATool.decrypt(enkey, serverPrivateKey);
            return AESTool.decrypt(endata, aseKey);
        } catch (Exception ex) {
            if (log.isDebugEnabled()) {
                log.debug("{}", ex.getMessage());
            }
            return StringUtils.EMPTY;
        }
    }


}
